extends CharacterBody3D

@export var inventory: InventoryData

@export var max_health: int = 20
@export var max_mana: int = 20
@export var max_hunger: int = 20
@export var max_thirst: int = 20
@export var health: int = 20
@export var mana: int = 20
@export var hunger: int = 20
@export var thirst: int = 20

const SPEED = 5.0
const RUN_SPEED = 8.0
const JUMP_VELOCITY = 4.5

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
@onready var neck := $Neck
@onready var camera := $Neck/Camera3D
@onready var interact_ray = $Neck/Camera3D/RayCast3D

signal toggle_inventory()

func _ready():
	PlayerManager.player = self

func _unhandled_input(event):
	if event.is_action_pressed("ui_cancel"):
		Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
		if $"../CanvasLayer/InventoryInterface".visible:
			toggle_inventory.emit()
		if $"..".paused:
			$"../CanvasLayer/HotBar".show()
		else:
			$"../CanvasLayer/HotBar".hide()
	if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
		if event is InputEventMouseMotion:
			neck.rotate_y(-event.relative.x * 0.003)
			camera.rotate_x(-event.relative.y * 0.003)
			camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-90), deg_to_rad(90))
	if Input.is_action_just_pressed("inventory"):
		toggle_inventory.emit()
	if Input.is_action_just_pressed("interact"):
		interact()

func _physics_process(delta):
	# Add the gravity.
	if not is_on_floor():
		velocity.y -= gravity * delta

	# Handle Jump.
	if Input.is_action_pressed("jump") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var input_dir = Input.get_vector("move left", "move right", "move forward", "move back")
	var direction = (neck.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
	if direction:
		if Input.is_action_pressed("move fast"):
			velocity.x = direction.x * RUN_SPEED
			velocity.z = direction.z * RUN_SPEED
		else:
			velocity.x = direction.x * SPEED
			velocity.z = direction.z * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)
		velocity.z = move_toward(velocity.z, 0, SPEED)

	move_and_slide()

func interact():
	if interact_ray.is_colliding():
		if interact_ray.get_collider().is_in_group("external_inventory") \
				or interact_ray.get_collider().is_in_group("pick_upable"):
			interact_ray.get_collider().player_interact()

func get_drop_position() -> Vector3:
	var direction = -camera.global_transform.basis.z
	return camera.global_position + direction

func heal(hunger_restore: int, health_restore: int, mana_restore: int, thirst_restore: int):
	hunger += hunger_restore
	if hunger > max_hunger:
		hunger = max_hunger
	health += health_restore
	if health > max_health:
		health = max_health
	mana += mana_restore
	if mana > max_mana:
		mana = max_mana
	thirst += thirst_restore
	if thirst > max_thirst:
		thirst = max_thirst

func save_dict():
	var dict = {
		"pos" : {
			"x" : position.x,
			"y" : position.y,
			"z" : position.z
		},
		"health" : health,
		"mana" : mana,
		"hunger" : hunger,
		"thirst" : thirst
	}
	return dict

func load_dict(dict: Dictionary):
	health = dict['health']
	mana = dict['mana']
	hunger = dict['hunger']
	thirst = dict['thirst']
	position.x = dict['pos']['x']
	position.y = dict['pos']['y']
	position.z = dict['pos']['z']

func save_inventory_array():
	var array = []
	for i in inventory.slot_datas:
		if i is SlotData:
			var dict: Dictionary = {
				"quantity": i.quantity,
				"item_id": i.item_data.id
			}
			array.append(dict)
		else:
			array.append(null)
	return array

func load_inventory_array(array: Array, items: Array[ItemData]):
	var items_dict = {}
	for i in items:
		items_dict[i.id] = i
	for i in range(array.size()):
		if array[i] != null:
			var slot = SlotData.new()
			if items_dict.has(array[i]['item_id']):
				slot.item_data = items_dict[array[i]['item_id']]
			else:
				slot.item_data = items_dict["corrupt"]
			slot.quantity = array[i]['quantity']
			inventory.slot_datas[i] = slot
		else:
			inventory.slot_datas[i] = null
